package emulator

import (
	"config"
	"crypto/rand"
	"encoding/binary"
	"encoding/json"
	"fmt"
	"github.com/gorilla/websocket"
	"log"
	"mail"
	"time"
)

// var (
// 	devservers = []string{"ws://ocean.llwant.com:17273"}
// )

type ServerMsgHead struct {
	Uri string `json:"uri"`
}

// 因为用户与设备的Controller字段不完全相同，因此不能共用

func sendMessage(c *websocket.Conn, buf []byte) {
	err := c.WriteMessage(websocket.TextMessage, buf)
	if err != nil {
		log.Println("write:", err)
		return
	}
}

func runtimeInfoReply(c *websocket.Conn, msg []byte) {
	log.Println("relay msg:", string(msg))
	type Relay struct {
		Action string `json:"action"`
		Uri    string `json:"uri"`
	}

	var relay Relay
	err := json.Unmarshal(msg, &relay)
	if err != nil {
		log.Println("onServerMessage , json decode failed:", err)
		return
	}

	type RunTime struct {
		Uri      string `json:"uri"`
		Relay    string `json:"relay"`
		Meastate string `json:"meastate"`
		Power    string `json:"power"`
		Voltage  string `json:"voltage"`
		Current  string `json:"current"`
	}

	runTime := RunTime{}
	runTime.Uri = "/runtimeInfo"
	runTime.Relay = "break"
	runTime.Meastate = "idle"
	runTime.Power = "0:0"
	runTime.Voltage = "0:0"
	runTime.Current = "NaN"

	if relay.Uri == "/relay" && relay.Action != "" {
		runTime.Relay = relay.Action
	}

	buf, err := json.Marshal(runTime)
	if err != nil {
		log.Println("encode err: ", err)
		return
	}

	sendMessage(c, buf)
}

func onKeepalive(c *websocket.Conn) {
	serverMsgHead := ServerMsgHead{}
	serverMsgHead.Uri = "/kr"

	buf, err := json.Marshal(serverMsgHead)
	if err != nil {
		log.Println("encode err: ", err)
		return
	}

	sendMessage(c, buf)
}

func onServerMessage(c *websocket.Conn, msg []byte) {
	var msgHead ServerMsgHead
	err := json.Unmarshal(msg, &msgHead)
	if err != nil {
		log.Println("onServerMessage , json decode failed:", err)
		return
	}

	serverAddress := c.RemoteAddr().String()

	switch msgHead.Uri {
	case "/ka":
		onKeepalive(c)
		log.Printf("on server %s keepAlive msg: %s", serverAddress, string(msg))
	case "/kr":
		log.Printf("on server %s keepAlive reply: %s", serverAddress, string(msg))
		break
	case "/loginReply":
		log.Printf("on server %s login reply, msg: %s", serverAddress, string(msg))
		break
	case "/getRuntime":
		runtimeInfoReply(c, msg)
		break
	case "/relay":
		runtimeInfoReply(c, msg)
		break
	default:
		log.Printf("on server %s unkonw message: %s", serverAddress, string(msg))
	}

}

func randKey() int32 {
	var n int32
	binary.Read(rand.Reader, binary.LittleEndian, &n)
	if n < 0 {
		return -n
	}

	return n
}

func createController() interface{} {
	type Controller struct {
		Id      string `json:"id"`
		Account string `json:"account"`
		CType   string `json:"type"`
		Apptype string `json:"apptype"`

		DeviceName        string `json:"deviceName"`
		DeviceVersion     string `json:"deviceVersion"`
		DeviceVersionCode int    `json:"deviceVersionCode"`

		FirmName        string `json:"firmName"`
		FirmVersion     string `json:"firmVersion"`
		FirmVersionCode int    `json:"firmVersionCode"`

		Key   int    `json:"key"`
		Relay string `json:"relay"`
	}

	controller := Controller{}
	controller.Id = "eefa9dcc-a1a7-4547-bae7-59e94ebb292f"
	controller.Account = config.Account
	controller.CType = "wifi-switch"
	controller.Apptype = "switch-measure"

	controller.DeviceName = "vesync_wifi_outlet"
	controller.DeviceVersion = "1.0"
	controller.DeviceVersionCode = 1

	controller.FirmName = "cosytek_firm_a"
	controller.FirmVersion = "1.0"
	controller.FirmVersionCode = 1

	controller.Key = int(randKey())

	// dev, ok := devs[controller.Id]
	// if ok {
	// 	controller.Relay = dev.Relay
	// } else {
	// 	controller.Relay = "break"
	// }

	return controller

}

func sendKeepAliveMsg(c *websocket.Conn) {
	serverMsgHead := ServerMsgHead{}
	serverMsgHead.Uri = "/ka"

	buf, err := json.Marshal(serverMsgHead)
	if err != nil {
		log.Println("encode err: ", err)
		return
	}

	sendMessage(c, buf)
}

func keepAliveWS(c *websocket.Conn, ch <-chan int) {
	var flag int = 0
	ticker := time.NewTicker(3 * time.Minute)
	for {
		select {
		case <-ticker.C:
			sendKeepAliveMsg(c)

			flag++
			if flag > 2 {
				//send notify
				//TODO
				serverAddress := c.RemoteAddr().String()
				errMsg := fmt.Sprintf("err: server %s is down", serverAddress)
				mail.SendMail(subject, errMsg)
				log.Println("server is down")
			}

		case flag = <-ch:
			if flag == 3 {
				return
			}

			log.Println("clean flag")
		}
	}
}

func createDevices(serverUrl string) {
	log.Printf("create device websocket connect to %s", serverUrl)
	c, _, err := websocket.DefaultDialer.Dial(serverUrl+"/gnws", nil)
	if err != nil {
		log.Fatal("dial:", err)
	}
	defer c.Close()

	controller := createController()
	buf, err := json.Marshal(controller)
	if err != nil {
		log.Println("encode err: ", err)
		return
	}

	err = c.WriteMessage(websocket.TextMessage, buf)
	if err != nil {
		log.Println("write:", err)
		return
	}

	keepAliveC := make(chan int)
	go keepAliveWS(c, keepAliveC)

	for {
		_, message, err := c.ReadMessage()
		if err != nil {
			log.Println("read:", err)
			keepAliveC <- 3
			return
		}

		keepAliveC <- 0
		onServerMessage(c, message)
	}

}
